/*
 * @Author: your name
 * @Date: 2021-09-27 09:25:25
 * @LastEditTime: 2021-10-05 20:45:51
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \bigdatavue\src\views\others\flowechart\flowechart.ts
 */

import { Graph, Addon, FunctionExt, Vector, DataUri } from '@antv/x6'
import type { EdgeView, Cell, Node } from '@antv/x6'
import { useMassage } from '@/ntils/hooks'
import { graphConfig } from './flowconfig'
import type { Ref, ComputedRef } from 'vue'
import { computed, unref, ref } from 'vue'
// 画布缩放
let scale = ref<number>(1)
// 显示JSON
let showJson = ref<Boolean>(false)  
// JSON数据
let JSONdata = ref()  
// 当前选中信息
let selectData = ref()
// 侧边栏禁用状态
let disables = ref()
const { Dnd } = Addon
// 将基础 配置添加到此处，基础config,
// 添加， 在外面声明添加类并继承主程FlowBaseic
let { createMessage } = useMassage()
interface MenuNode{
    node:any,
    x:Number | String,
    y:Number | String,
    show:Boolean
}
interface CopyOptions{
  offset:Number,
  useLocalStorage: Boolean,
}
interface GlobalStyle{
        color?:Partial<String>
        width?:Partial<String | Number>,
        height?:Partial<String | Number>,
}
/**
 * @description: 
 * @param {*}
 * @return {*} 默认值color
 */
interface Options{
       globalStyle:GlobalStyle
}
/**
 * @description: 
 * @param {*}
 * @return {*} refContainer面板内容，nodemenu节点菜单，graph实例，node节点，dnd拖拽
 */
class FlowBaseic {
    refContainer:Ref<HTMLDivElement>

    nodemenu:HTMLDivElement

    copyOption:CopyOptions

    graph:any

    node:any

    dnd:any

    zoom:number

    constructor(container:Ref<HTMLDivElement>, nodemenu:HTMLDivElement, options = {
        globalStyle: {
            color: '#008c8c',
            width: innerWidth,
            height: innerHeight,
        } as GlobalStyle,
    } as Options) {
        this.node = null
        this.refContainer = container
        this.nodemenu = nodemenu
        this.copyOption = {
            offset: 30,
            useLocalStorage: false,
        }
        this.zoom = 0
        this.init(options)
    }

    // 初始化
    init(options:Options):void {
        let _this = this
        let { color, width, height } = options.globalStyle
        this.graph = new Graph(graphConfig(this.refContainer, width, height))
        this.graph.drawBackground({
            color,
        })
        this.dnd = new Dnd({
            target: _this.graph,
            scaled: false,
            animation: true,
        }) as Addon.Dnd  
        // 链接鼠标移入出现删除Tools 
        this.graph.on('edge:mouseenter', ({ edge }) => {
            edge.addTools([
                'source-arrowhead',
                'target-arrowhead',
                {
                    name: 'button-remove',
                    args: {
                        distance: -30,
                    },
                },
            ])
        }) 
        this.graph.on('edge:mouseleave', ({ edge }) => {
            edge.removeTools()
        })
        // 链接线鼠标移入触发,可拖拽的线
        this.graph.on('cell:mouseenter', ({ cell }) => {
            // 链接添加动态拖拽
            // 动态添加
            if (!cell.isNode()) {
                cell.addTools(['vertices', 'segments'])
            }
        }) 
        // 鼠标移入node效果
        this.graph.on('node:mouseenter', (args) => {
            args.node.attr({
                body: {
                    fill: '#a1cbf1',
                          
                },
                     
            })
        }) 
        // 鼠标移出效果
        this.graph.on('node:mouseleave', (args) => {
            args.node.attr({
                body: {
                    fill: '#fff',
                },
                      
            })
        })
        //  鼠标按下清除Tip
        this.graph.on('node:mousedown', () => {
            this.changePosition({ node: this.nodemenu, x: 0, y: 0, show: false })
        }) 
        // 点击节点
        this.graph.on('node:click', FunctionExt.debounce((nodeAttr: any) => {
            _this.node = nodeAttr.node
            this.editnode(nodeAttr)
            this.changePosition({ node: this.nodemenu, x: 0, y: 0, show: false })
        }), 500)
        // 右击节点
        this.graph.on('blank:click', () => {
            let nodes = this.graph.getNodes()
            this.node = null
            selectData.value = null
            nodes.forEach(item => item.removeTools())
            this.changePosition({ node: this.nodemenu, x: 0, y: 0, show: false })
        }) 
        // 监听右键点击显示Menu菜单
        this.graph.on('node:contextmenu', (args) => {
            if (this.nodemenu) {
                this.node = args
                this.changePosition({ node: this.nodemenu, x: args.x, y: args.y, show: true })   
            }
        })
        // 自定义执行函数
        this.graph.on('handle', (cell:Cell) => {
            // 连贯触发
            if (cell.isEdge()) {
                const view = this.graph.findViewByCell(cell) as EdgeView
                if (view) {
                    const token = Vector.create('circle', { r: 6, fill: '#feb662' })
                    //  获取下一个目标 并触发下一个 连线动画
                    const target = cell.getTargetCell()
                    const nodes = cell.getTargetNode()
                    //  每个动画执行1s
                    setTimeout(() => {
                        view.sendToken(token.node, 1000, () => {
                            if (target) {
                                // 执行成功后设置状态
                                nodes?.attr({
                                    body: {
                                        stroke: '#01fb52',
                                        strokeDasharray: '5, 1',
                                        strokeDashoffset: 10,
                                    },
                                })
                                // 触发事件
                                this.graph.trigger('handle', target)
                            }
                        })
                    }, 300)
                }
            } else {
                const edges = this.graph.model.getConnectedEdges(cell, {
                    outgoing: true,
                })
                edges.forEach((edge) => this.graph.trigger('handle', edge)) 
            }
        })
        // 监听鼠标滚轮并获取缩放值
        this.graph.on('scale', (args) => {
            scale.value = args.sx
        })
        // 键盘绑定事件-复制粘贴
        this.graph.bindKey('ctrl+c', () => {
            this.nodeCopy()
            return false
        })
        this.graph.bindKey('ctrl+v', () => {
            this.nodeCut()
            return false
        })
        // 键盘绑定--撤销、字体加粗,删除节点,执行
        this.graph.bindKey('ctrl+z', () => {
            this.undo()
            return false
        })
        this.graph.bindKey('ctrl+f', () => {
            this.attrWeight()
            return false
        })
        this.graph.bindKey('ctrl+d', () => {
            let node = this.graph.getSelectedCells()
            this.removeNode({ node: node[0] })
            return false
        })
        this.graph.bindKey('ctrl+q', () => {
            let node = this.graph.getSelectedCells()
            if (node.length < 1) {
                createMessage.info('请重新选择一个节点执行')
                return false
            }
            this.executeNode({ node: node[0] })
            return false
        })
        // 展示json
        this.graph.bindKey('ctrl+alt+d', () => {
            this.openJson(true)
            return false
        })
    }

    // Menu事件
    handleClick(e:any):void {
        let args = this.node
        switch (e.id) {
            // 删除节点;
            case 'remove':
                this.removeNode(args); break
            // 执行； 
            case 'todo':
                this.executeNode(args); break 
            // 编辑
            case 'edit':
                this.editnode(args); break  
        }
        // 每次执行完以上逻辑隐藏菜单栏
        this.changePosition({ node: this.nodemenu, x: 0, y: 0, show: false })  
    }

    // 增加节点
    addNode(nodes:Node.Metadata, e:MouseEvent):void {
        let doms = nodes
        const node = this.graph.createNode(doms)
        this.dnd.start(node, e)
    }

    // 删除
    removeNode(args) {
        //  删除node节点 
        this.graph.removeNode(args.node)
        //  删除链接线
        this.graph.removeConnectedEdges(args.node)  
    }

    // 复制
    nodeCopy() {
        const cells = this.graph.getSelectedCells()
        if (cells && cells.length) {
            this.graph.copy(cells, this.copyOption)
            createMessage.success('复制成功')
        } else {
            createMessage.info('请先选中节点再复制')
        }
    }

    // 粘贴
    nodeCut() {
        if (this.graph.isClipboardEmpty()) {
            createMessage.info('剪切板为空，不可粘贴')
        } else {
            const cells = this.graph.paste(this.copyOption)
            this.graph.cleanSelection()
            this.graph.select(cells)
            createMessage.success('粘贴成功')
        }
    }

    // 执行
    executeNode(args) {
        if (this.graph.isLeafNode(args.node)) {
            createMessage.error('执行节点不能为没有可输出的节点')
            return false
        }
        this.graph.trigger('handle', args.node) 
    }

    // 撤销
    undo() {
        this.graph.undo()
    }

    // 重做
    redoContent() {
        this.graph.redo()  
    }

    // 编辑
    editnode(args) {
        let nodes = this.graph.getNodes()
        nodes.forEach(item => item.removeTools())
        // 设置当前选中状态
        args.node.addTools({
            name: 'boundary',
            args: {
                attrs: {
                    stroke: '#f40',
                    'stroke-width': 1,
                    'fill-opacity': 0.2,
                },
            },
        })
        selectData.value = args.node.attrs
    }

    // 缩放
    contentZoom(e) {
        let Ozoom = Number(scale.value.toFixed(1))
        if (e < 0 && (Ozoom - 0.1) >= 0.5) {
            this.graph.zoom(0.1 * e)
        } else if (e > 0 && (Ozoom + 0.1) <= 2) {
            this.graph.zoom(0.1 * e)
        }
    }

    // 更改背景颜色
    changeBgcolor(color) {
        this.graph.drawBackground({ color })
    }

    // 字体加粗
    attrWeight() {
        //  args.node.attr('label/fontWeight', 'bold')
        //  两者等同
        if (this.node) {
            this.node.attr('label/fontWeight', 'bold')
        } else {
            createMessage.error('操作失败！')
        }
    }

    // 背景设置?
    settingBg(color) {
        this.graph.drawBackground({
            color,
        })  
    }

    // 节点文本?
    settingText(e:String) {
        const cells = this.graph.getSelectedCells()
        if (cells) {
            this.node.attr('text/text', e)
        } else {
            createMessage.error('操作失败！')
        } 
    }

    // 字体大小?
    settingTextSize(e:[Number | String]) {
        const cells = this.graph.getSelectedCells()
        if (cells) {
            this.node.attr('label/fontSize', e)
        } else {
            createMessage.error('操作失败！')
        }
    }

    // 字体颜色?
    settingTextColor(e:String = '#666') {
        const cells = this.graph.getSelectedCells()
        if (cells) {
            this.node.attr({
                label: {
                    fill: e,
                },
            })
        } else {
            createMessage.error('操作失败！')
        }
    }

    // 字体下划线
    settingTextundlin(e:['underline' | 'noraml']) {
        const cells = this.graph.getSelectedCells()
        if (cells) {
            this.node.attr('label/textDecoration', e)
        } else {
            createMessage.error('操作失败！')
        }
    }

    // 斜体
    settingTextstyle(e:['italic' | 'noraml']) {
        const cells = this.graph.getSelectedCells()
        if (cells) {
            this.node.attr('label/fontStyle', e)
        } else {
            createMessage.error('操作失败！')
        }
    }

    // 线条颜色?
    settingLineColor() {}

    // toSVG
    toSvg() {
        this.graph.toSVG((dataUri: string) => {
        // 下载
            console.log(dataUri)
            DataUri.downloadDataUri(DataUri.svgToDataUrl(dataUri), 'chart.svg')
        })
    }

    // toJPG/png
    toPngJpg() {
        this.graph.toPNG((dataUri: string) => {
        // 下载
            DataUri.downloadDataUri(dataUri, 'chart.png')
        })
    }

    // 输出json
    toJson():void {
        let sjson = this.graph.toJSON()
        JSONdata.value = sjson
    }

    // 展示JSON试图
    openJson(e:Boolean):void {
        this.toJson()
        showJson.value = e
    }

    // 当前选中节点信息
    getDateinfo():ComputedRef<Boolean> {
        let value = computed(() => unref(selectData.value)) 
        return value
    }

    // 导出JSON
    getJsonData():ComputedRef<Boolean> {
        let value = computed(() => unref(JSONdata.value)) 
        return value
    }

    // 导出显示visible
    getShowJson():ComputedRef<Boolean> {
        let value = computed(() => unref(showJson.value)) 
        return value
    }

    // 导出缩放值
    getZoomValue():ComputedRef<number> {
        let value = computed(() => unref(scale.value)) 
        return value
    }

    // nodeMenu位置调整与显示隐藏
    changePosition({ node, x, y, show }:MenuNode):void {
        // 位置随着缩放大小自适应
        node.style.visibility = show ? 'visible' : 'hidden'
        node.style.top = (2 - scale.value) * y + 'px'
        node.style.left = (2 - scale.value) * x + 'px'
    }
}
export const graphExample = (container:Ref<HTMLDivElement>, nodemenu:HTMLDivElement, options:Options) => {
    let graphs = new FlowBaseic(container, nodemenu, options)
    return {
        graphs,  
    }
}
